package com.xq.tmall.util;

import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.lang.NonNull;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * [BeanUtils工具类]
 *
 * @author zhangsa
 * @since JDK 1.8
 */
public class BeanUtil {

    // Spring 类型转换服务（内置常用转换器，如 String→Number、Date 等）
    private static final ConversionService CONVERSION_SERVICE = new DefaultConversionService();

    // 自定义转换器注册表
    private static final Map<Class<?>, Map<Class<?>, Converter<?, ?>>> CUSTOM_CONVERTERS = new HashMap<>();

    /**
     * 注册自定义转换器（支持泛型）
     * @param sourceType 源类型
     * @param targetType 目标类型
     * @param converter 转换器
     * @param <S> 源类型泛型
     * @param <T> 目标类型泛型
     */
    public static <S, T> void registerConverter(Class<S> sourceType, Class<T> targetType, Converter<S, T> converter) {
        CUSTOM_CONVERTERS.computeIfAbsent(sourceType, k -> new HashMap<>())
                .put(targetType, converter);
    }

    /**
     * 复制源对象的属性到目标对象（自动匹配字段名，支持类型转换）
     * @param source 源对象
     * @param target 目标对象（需已初始化）
     */
    public static void copyProperties(@NonNull Object source, @NonNull Object target) {
        // 使用 Spring BeanUtils 复制基础属性（字段名和类型完全匹配）
        BeanUtil.copyProperties(source, target);

        // 处理需要类型转换的字段（字段名相同但类型不同）
        Class<?> sourceClass = source.getClass();
        Class<?> targetClass = target.getClass();

        // 获取源对象和目标对象的所有字段（包括父类）
        Set<String> sourceFields = getAllFieldNames(sourceClass);
        Set<String> targetFields = getAllFieldNames(targetClass);

        // 遍历公共字段（名称相同）
        for (String fieldName : sourceFields) {
            if (!targetFields.contains(fieldName)) continue;

            // 获取源字段值和目标字段类型
            Object sourceValue = getFieldValue(source, fieldName);
            Class<?> targetFieldType = getFieldClass(targetClass, fieldName);

            if (sourceValue == null) continue; // 空值不处理

            // 尝试类型转换
            try {
                Object convertedValue = convertValue(sourceValue, targetFieldType);
                setFieldValue(target, fieldName, convertedValue);
            } catch (Exception e) {
                throw new RuntimeException(String.format(
                        "字段 %s 转换失败（源类型：%s → 目标类型：%s）",
                        fieldName, sourceValue.getClass(), targetFieldType), e);
            }
        }
    }



    public static <T> List<List<T>> splitsList(List<T> list, int perSize) {
        if (CollectionUtils.isEmpty(list)) {
            return Collections.EMPTY_LIST;
        }

        List<List<T>> tNewList = new ArrayList<>();
        int priIndex;
        int lastPriIndex;
        int insertTimes = list.size() / perSize;
        for (int i = 0; i <= insertTimes; i++) {
            priIndex = perSize * i;
            lastPriIndex = priIndex + perSize;
            List<T> subList;
            if (i == insertTimes) {
                subList = list.subList(priIndex, list.size());
            } else {
                subList = list.subList(priIndex, lastPriIndex);
            }
            if (subList.size() > 0) {
                tNewList.add(subList);
            }
        }
        return tNewList;
    }

    /**
     * 将 POJO 转换为 Map（可选是否忽略空值）
     * @param pojo 待转换的 POJO 对象
     * @param ignoreNull 是否忽略值为 null 的字段
     * @return 字段名到值的映射 Map
     */
    @NonNull
    public static Map<String, Object> convertPojoToMap(@NonNull Object pojo, boolean ignoreNull) {
        BeanWrapper beanWrapper = new BeanWrapperImpl(pojo);
        return Arrays.stream(beanWrapper.getPropertyDescriptors())
                .filter(pd -> !"class".equals(pd.getName())) // 排除 "class" 字段
                .collect(Collectors.toMap(
                        PropertyDescriptor::getName,
                        pd -> {
                            Object value = beanWrapper.getPropertyValue(pd.getName());
                            return (ignoreNull && value == null) ? null : value;
                        }
                ));
    }

    // ------------------------------ 私有工具方法 ------------------------------

    /**
     * 递归设置嵌套对象的字段值（如 `user.address.city`）
     */
    private static void setObjectField(Object target, String[] fieldPath, int index, Object value) {
        if (index >= fieldPath.length) return;

        String currentField = fieldPath[index];
        Object currentValue = getFieldValue(target, currentField);

        if (currentValue == null) {
            // 创建嵌套对象实例（假设无参构造）
            Class<?> currentClass = target.getClass();
            try {
                currentValue = currentClass.getDeclaredField(currentField).getType().newInstance();
                setFieldValue(target, currentField, currentValue);
            } catch (Exception e) {
                throw new RuntimeException(String.format(
                        "嵌套字段 %s 初始化失败（类型：%s）", currentField, currentClass), e);
            }
        }

        if (index == fieldPath.length - 1) {
            // 最终字段，设置值
            setFieldValue(currentValue, fieldPath[index], value);
        } else {
            // 递归处理下一级字段
            setObjectField(currentValue, fieldPath, index + 1, value);
        }
    }

    /**
     * 获取对象的字段值
     */
    private static Object getFieldValue(Object obj, String fieldName) {
        try {
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            return field.get(obj);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(String.format("获取字段 %s 失败", fieldName), e);
        }
    }

    /**
     * 设置对象的字段值（自动类型转换）
     */
    private static void setFieldValue(Object obj, String fieldName, Object value) {
        try {
            Class<?> targetType = getFieldClass(obj.getClass(), fieldName);
            Object convertedValue = convertValue(value, targetType);
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(obj, convertedValue);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(String.format("设置字段 %s 失败", fieldName), e);
        }
    }

    /**
     * 获取字段的类型（支持父类字段）
     */
    private static Class<?> getFieldClass(Class<?> clazz, String fieldName) {
        try {
            return clazz.getDeclaredField(fieldName).getType();
        } catch (NoSuchFieldException e) {
            // 递归查找父类字段
            Class<?> superClass = clazz.getSuperclass();
            if (superClass != Object.class) {
                return getFieldClass(superClass, fieldName);
            }
            throw new RuntimeException(String.format("字段 %s 不存在于类 %s 或其父类中", fieldName, clazz));
        }
    }

    /**
     * 类型转换（优先使用自定义转换器，其次使用 Spring ConversionService）
     */
    private static <T> T convertValue(Object source, Class<T> targetType) {
        if (source == null) return null;

        // 1. 检查是否类型相同
        if (targetType.isInstance(source)) {
            return targetType.cast(source);
        }
        // 3. 使用 Spring ConversionService
        return CONVERSION_SERVICE.convert(source, targetType);
    }

    /**
     * 获取类的所有字段名（包括父类）
     */
    private static Set<String> getAllFieldNames(Class<?> clazz) {
        Set<String> fields = new HashSet<>();
        while (clazz != Object.class) {
            fields.addAll(Arrays.stream(clazz.getDeclaredFields())
                    .map(Field::getName)
                    .collect(Collectors.toSet()));
            clazz = clazz.getSuperclass();
        }
        return fields;
    }
}
